追寻梦想的路

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

对于并发工作,你永远不知道一个线程何时运行,你需要某种方式来避免两个任务访问相同的资源,即要避免资源竞争,至少在关键代码上不能出现这样的情况,否则多个线程同时对某个内存区域操作会导致数据破坏。

程序代码中的临界区是需要互斥访问的,同一时刻只能有一个线程来访问临界区,也就是线程对临界区的访问时互斥的。

竞争条件:当多个线程同时访问某个共享的内存区域并且对其进行读写操作时,就会出现数据破坏。这就是竞争条件。避免竞争条件的方法是synchronized加锁。

样例,设有一个现成,该线程的任务是对共享变量count值+1。设count值的初始值为0,开辟1000个相同的线程,那么线程执行完后,count的值应该为1000(因为每个线程中都对count做了加一操作),但是实际效果会出现错误,这是因为出现了竞争条件。

解决办法:使用synchronized锁,

先定义一个object对象:

private final static Object lockObj = new Object();

下面即是同步块,在同步块中进行关键操作。

					synchronized(lockObj)
					{
						syn.count = syn.count+1;	
						temp = temp+1;
					}

  示例代码:

package ex5_Synchronize;

public class SynchronizeTest {
    
    //volatile方法可以保证每次都会去内存中读取变量的值,即遵守happen-befor原则,但是骑兵不能保证并发时数据不被破坏
    public volatile static int temp = 0;
    
    private final static Object lockObj = new Object();
    
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        
        
        Thread threads[] = new Thread[1000];
        
        for(int i=0;i<1000;i++)
        {
            threads[i] = new Thread(new Runnable() {
                
                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    //使用synchronize上锁,保证线程互斥访问

                    synchronized(lockObj)
                    {
                        syn.count = syn.count+1;    
                        temp = temp+1;
                    }
                }
            });
            threads[i].start();    
        }
        
        //调用join,让主线程等待当前线程执行完毕后再执行
        for(int i = 0;i<1000;i++)
        {
            try {
                threads[i].join();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
        //如果没有thread.join,那么其他线程还没执行完毕,主线程就要结束了,此时print输出的值并不是所有线程执行完后的值,所以不能说明问题
        System.out.print("count的值:"+syn.count+"\n");
        System.out.print("temp的值:"+temp);                
    }
}

输出结果

count的值:1000
temp的值:1000

 

-------------------------------------------------------------------------------

下面是正题

JAVA中synchronized关键字能够作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块。假如再细的分类,synchronized可作用于instance变量、object reference(对象引用,例如this)、static函数和class literals(类名称字面常量)身上。下面讨论synchronized用到不同地方对代码产生的影响:
假设P1、P2是同一个类的不同对象,这个类中定义了以下几种情况的同步块或同步方法,P1、P2就都能够调用他们。
1.把synchronized当作函数修饰符时,示例代码如下:
public synchronized void method(){
//….
}
这也就是同步方法,那这时synchronized锁定的是哪个对象呢?他锁定的是调用这个同步方法对象。也就是说,当一个对象P1在不同的线程中执行这个同步方法时,他们之间会形成互斥,达到同步的效果。但是这个对象所属的Class所产生的另一对象P2却能够任意调用这个被加了synchronized关键字的方法。上边的示例代码等同于如下代码:
public void method()
{
synchronized (this) // (1)
{
//…..
}
}
(1)处的this指的是什么呢?他指的就是调用这个方法的对象,如P1。可见,同步方法实质是将synchronized作用于Object Reference。那个拿到了P1对象锁的线程,才能够调用P1的同步方法,而对P2而言,P1这个锁和他毫不相干,程式也可能在这种情形下摆脱同步机制的控制,造成数据混乱。
2.同步块,示例代码如下:
public void method(SomeObject so) {
synchronized(so)
{
//…..
}
}
这时,锁就是so这个对象,谁拿到这个锁谁就能够运行他所控制的那段代码。当有一个明确的对象作为锁时,就能够这样写程式,但当没有明确的对象作为锁,只是想让一段代码同步时,能够创建一个特别的instance变量(它得是个对象)来充当锁:
class Foo implements Runnable
{
private byte[] lock = new byte[0]; // 特别的instance变量
Public void method()
{
synchronized(lock) { //… }
}
//…..
}
注:零长度的byte数组对象创建起来将比任何对象都经济。查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。
3.将synchronized作用于static 函数,示例代码如下:
Class Foo
{
public synchronized static void method1() // 同步的static 函数
{
//….
}
public void method2()
{
synchronized(Foo.class) // class literal(类名称字面常量)
}
}
代码中的method2()方法是把class literal作为锁的情况,他和同步的static函数产生的效果是相同的,取得的锁很特别,是当前调用这个方法的对象所属的类(Class类,而不再是由这个Class产生的某个具体对象了)。

转载请注明:struts教程网 » synchronized的深刻认识

 

 

如何来定义共享变量:

1、在主类中定义一个final变量,让其在其他线程中也可以执行。

2、使用static定义线程公用的成员变量,在一个类实现runnable接口,其中定义一个static变量,这个变量就为所有使用此接口的类对象所共用,即所有的类对象共用一份这个变量的拷贝。

 

1、java中synchronize可以用来给对象、代码块等加锁。synchronize关键字可以出现在函数外,也可以出现在函数体

2、没有volatile就没有java多线程

3、Thread.join()可以设置让当前线程执行完毕后再执行其他线程,这在控制线程的执行顺序上很有用。

下面通过一个代码来描述java多线程中可能出现的资源竞争条件(race condition)问题。

 

posted on 2015-03-11 22:01  追寻梦想的路  阅读(376)  评论(0编辑  收藏  举报